package cool.doudou.doudada.pay.core.signer;

import cool.doudou.doudada.cipher.algorithm.enums.SignAlgorithm;
import cool.doudou.doudada.cipher.algorithm.util.SignatureUtil;
import cool.doudou.doudada.pay.core.memory.WxPayMem;

import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;

/**
 * 微信签名者
 *
 * @author jiangcs
 * @since 2022/07/01
 */
public class WxSigner {
    /**
     * 获取 授权信息
     *
     * @param mchId                  商户Id
     * @param privateKeySerialNumber 商户API密钥证书序列号
     * @param reqMethod              请求方法：GET｜POST｜PUT｜DELETE
     * @param reqAbsoluteUrl         请求地址
     * @param reqParam               请求参数
     * @param reqBody                请求Body数据：POST｜PUT时传入对应值，其他传入空字符串
     * @return 授权字符串
     */
    public static String getAuthorization(String mchId, String privateKeySerialNumber, String reqMethod, String reqAbsoluteUrl, Map<String, Object> reqParam, String reqBody) {
        String schema = "WECHATPAY2-SHA256-RSA2048";
        long timestamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");

        StringBuilder sbUrlParam = new StringBuilder();
        if (reqParam != null) {
            for (String key : reqParam.keySet()) {
                if (sbUrlParam.length() <= 0) {
                    sbUrlParam.append("?");
                } else {
                    sbUrlParam.append("&");
                }
                sbUrlParam.append(key).append("=").append(reqParam.get(key));
            }
        }
        reqAbsoluteUrl += sbUrlParam;

        // 构造签名字符串
        String signatureStr = reqMethod + "\n" +
                reqAbsoluteUrl + "\n" +
                timestamp + "\n" +
                nonceStr + "\n" +
                reqBody + "\n";
        // 签名值 signatureValue
        String signatureValue = computeSignatureValue(signatureStr);
        // 签名信息 signatureInfo
        String signatureInfo = "mchid=\"" + mchId + "\"," +
                "serial_no=\"" + privateKeySerialNumber + "\"," +
                "nonce_str=\"" + nonceStr + "\"," +
                "timestamp=\"" + timestamp + "\"," +
                "signature=\"" + signatureValue + "\"";
        // Authorization: 认证类型 签名信息
        return schema + " " + signatureInfo;
    }

    /**
     * 获取 支付签名
     *
     * @param appId      微信AppId
     * @param timestamp  时间戳
     * @param nonceStr   随机字符串
     * @param packageStr prepay_id参数值
     * @return 签名字符串
     */
    public static String getPayment(String appId, String timestamp, String nonceStr, String packageStr) {
        // 构造签名字符串
        String signatureStr = appId + "\n" +
                timestamp + "\n" +
                nonceStr + "\n" +
                packageStr + "\n";
        // 签名值 signatureValue
        return computeSignatureValue(signatureStr);
    }

    /**
     * 计算签名值
     *
     * @param signatureStr 待签名字符串
     * @return 签名值
     */
    private static String computeSignatureValue(String signatureStr) {
        // 加密
        byte[] encryptArr = SignatureUtil.sign(SignAlgorithm.SHA256withRSA, signatureStr, WxPayMem.privateKey);

        // Base64
        return Base64.getEncoder().encodeToString(encryptArr);
    }

    /**
     * 证书验证
     *
     * @param certificate  平台证书
     * @param timestamp    时间戳
     * @param nonceStr     随机字符串
     * @param jsonStr      请求Body数据
     * @param signatureStr 签名字符串
     * @return true-验证通过；false-验证失败
     */
    public static boolean verify(X509Certificate certificate, String timestamp, String nonceStr, String jsonStr, String signatureStr) {
        try {
            // 构造签名字符串
            String sbSignatureValue = timestamp + "\n" +
                    nonceStr + "\n" +
                    jsonStr + "\n";

            Signature signature = Signature.getInstance("SHA256withRSA");
            signature.initVerify(certificate);
            signature.update(sbSignatureValue.getBytes(StandardCharsets.UTF_8));
            return signature.verify(Base64.getDecoder().decode(signatureStr));
        } catch (Exception e) {
            throw new RuntimeException("证书验证异常", e);
        }
    }
}
